/*
 *  FullBrevity.cpp
 *  IncrementalAlgorithm
 *
 *  Created by Roman Kutlak on 13/05/2011.
 *  Copyright 2011 Roman Kutlak. All rights reserved.
 *
 */

#include <iostream>

#include "FullBrevity.h"

using namespace std;

namespace kutlak {
namespace roman {
namespace gre {
    /////////////////////////////// PUBLIC   ///////////////////////////////////
    
#pragma mark -
#pragma mark lifecycle
    FullBrevity::FullBrevity(const pKnowledgeBase& kb,
                             const pOntology& ontology) : 
    Algorithm(kb, ontology, "Full Brevity") {}
    
    FullBrevity::~FullBrevity() = default;
    
#pragma mark -
#pragma mark operations
    //=============================  OPERATIONS  ===============================
    RefExp FullBrevity::MakeReferringExpression(const EntityPtrT& referent, 
                                                ContainerT distractors)
    {
        RefExp result;
        list<RefExp> queue;
        
        // check if referent is not null
        if (!referent)
            return result;
        
        // remove the referent from the set if present        
        EntityPtrEqPred pred(referent);
        auto refIt = std::remove_if(distractors.begin(), 
                                    distractors.end(), pred);
        distractors.erase(refIt, distractors.end());
//        distractors.resize(it - distractors.begin());
        
#ifdef DEBUG
        if (FB_DEBUG > 1)
            cerr << "FB::MRE(): Init distractors: " << distractors.size() << '\n';
#endif        
        // add type first
        if (referent->Type().Value() != DEFAULT_VALUE)
        {
            result.Add(referent->Type());
            Filter(distractors, referent->Type());
        }
#ifdef DEBUG  
        if (FB_DEBUG > 1)
            cerr << "FB::MRE(): after adding type: " << distractors.size() << '\n';
        size_t noOfExpr = 0;
#endif
        // if the type is sufficient, return the referring expression
        if (distractors.empty())
        {
            result.Prepend(Property("determiner", "definite"));
            return result;
        }
        
        // start with looking at individual properties
        for (auto it = referent->begin(); it != referent->end(); ++it)
        {
            RefExp re;
            if (!IsBannedAttribute(it->Attribute())) {
                re.Add(*it);
                queue.push_back(re);
            }
        }
        
        while (!queue.empty())
        {
            RefExp re = queue.front();
            queue.pop_front();
            
#ifdef DEBUG
            ++noOfExpr;
            if (FB_DEBUG > 2)
                cout << re.ToString() << " ";
#endif
            int extensionSize = ExtensionSize(distractors, re);
            
#ifdef DEBUG
            if (FB_DEBUG > 2)
                cout << "\n\textension size: " << extensionSize << endl;
#endif     
            if (extensionSize == 0 ||
                (extensionSize == 1 && *distractors.front() == *referent))
            {
                result.Add(re);
                distractors.clear();
                break;
            }
            else
            {
                // this could be sped up by avoiding doing all permutations
                for (auto it = referent->begin(); it != referent->end(); ++it)
                {
                    RefExp tmpRe = re;
                    Property prop(*it);
                    if (!IsBannedAttribute(prop.Attribute()))
                    {
                        if (tmpRe.Add(prop))
                        {
                            queue.push_back(tmpRe);
                        }
                    }
                }
            }
        }
        
        // if we did not find a shorter RE, just add all properties
        // and take that to be the result.
        if (2 > result.Size() && !distractors.empty())
        {
            for (auto it = referent->begin(); it != referent->end(); ++it)
            {
                Property prop(*it);
                if (!IsBannedAttribute(prop.Attribute()))
                {
                    result.Add(prop);
                }
            }
        }
        
#ifdef DEBUG
        if (FB_DEBUG > 0)
            cout << "\nnumber of expressions tried: " << noOfExpr << endl;
#endif 
        
        if (distractors.empty())
            result.Prepend(Property("determiner", "definite"));
        else
            result.Prepend(Property("determiner", "indefinite"));
        
        return result;
    }

    RefExp FullBrevity::MakeReferringExpression(const std::string& referentName)
    {
        return this->Algorithm::MRE(referentName);
    }
}
}
}
